package br.edu.unifei.cct720.trabalho08.modelo.formas;

import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;

import br.edu.unifei.cct720.trabalho08.modelo.ponto.Ponto2D;

public class Reta implements FormaGeometrica {

	// Declaracao de variaveis de instacia
	private Ponto2D pInicio;
	private Ponto2D pFinal;
	private Color cor;

	/**
	 * Construtor
	 * 
	 * @param ini
	 * @param pFinal
	 */
	public Reta(Ponto2D ppIniciocio, Ponto2D pFinal) {
		this(ppIniciocio, pFinal, Color.BLACK);
	}

	/**
	 * Construtor
	 * 
	 * @param pInicio
	 * @param pFinal
	 * @param cor
	 */
	public Reta(Ponto2D ppIniciocio, Ponto2D pFinal, Color cor) {
		if (ppIniciocio.getX() < pFinal.getX()
				|| (ppIniciocio.getX() == pFinal.getX() && ppIniciocio.getY() < pFinal.getY())) {
			this.pInicio = ppIniciocio;
			this.pFinal = pFinal;
		} else {
			this.pInicio = pFinal;
			this.pFinal = ppIniciocio;
		}
		this.setCor(cor);
	}

	/**
	 * Cria os pontos pelo DDA Inteiro
	 * 
	 * @return
	 */
	public List<Ponto2D> getPontos() {
		ArrayList<Ponto2D> pontos = new ArrayList<Ponto2D>();

		if (this.getTipo() == Tipo.HORIZONTAL) {
			pontos.add(this.pInicio);

			for (int x = this.pInicio.getX(); x <= this.pFinal.getX(); x++) {
				pontos.add(new Ponto2D(x, this.pInicio.getY(), getCor()));
			}

			pontos.add(this.pFinal);
			return pontos;
		}

		if (this.getTipo() == Tipo.VERTICAL) {
			pontos.add(this.pInicio);

			for (int y = this.pInicio.getY(); y <= this.pFinal.getY(); y++) {
				pontos.add(new Ponto2D(this.pInicio.getX(), y, getCor()));
			}

			pontos.add(this.pFinal);
			return pontos;
		}

		int dX = pFinal.getX() - pInicio.getX();
		int dY = pFinal.getY() - pInicio.getY();
		int erro = 0;
		int x = pInicio.getX();
		int y = pInicio.getY();

		if ((Math.abs(dY) >= Math.abs(dX) && pInicio.getY() > pFinal.getY())
				|| (Math.abs(dY) < Math.abs(dX) && (dY < 0))) {
			x = pFinal.getX();
			y = pFinal.getY();
			dX = pInicio.getX() - pFinal.getX();
			dY = pInicio.getY() - pFinal.getY();
		}

		pontos.add(pInicio);

		if (dX >= 0) { // Caso 1
			if (Math.abs(dX) >= Math.abs(dY)) {
				for (int i = 1; i < Math.abs(dX); i++) {
					if (erro < 0) {
						x++;
						erro += dY;
					} else {
						x++;
						y++;
						erro += dY - dX;
					}

					pontos.add(new Ponto2D(x, y, getCor()));
				}
			} else { // Caso 2
				for (int i = 1; i < Math.abs(dY); i++) {
					if (erro < 0) {
						x++;
						y++;
						erro += dY - dX;
					} else {
						y++;
						erro -= dX;
					}

					pontos.add(new Ponto2D(x, y, getCor()));
				}
			}
		} else { // Caso 3
			if (Math.abs(dX) >= Math.abs(dY)) {
				for (int i = 1; i < Math.abs(dX); i++) {
					if (erro < 0) {
						x--;
						erro += dY;
					} else {
						x--;
						y++;
						erro += dY + dX;
					}

					pontos.add(new Ponto2D(x, y, getCor()));
				}
			} else { // Caso 4
				for (int i = 1; i < Math.abs(dY); i++) {
					if (erro < 0) {
						x--;
						y++;
						erro += dX + dY;
					} else {
						y++;
						erro += dX;
					}

					pontos.add(new Ponto2D(x, y, getCor()));
				}
			}
		}

		pontos.add(pFinal);
		return pontos;
	}

	public enum Tipo {
		HORIZONTAL, VERTICAL, DIAGONAL, INCLINADA;
	}

	/**
	 * Encontra o tipo da reta: horizontal, vertical, diagonal ou inclinada
	 * 
	 * @return
	 */
	public Tipo getTipo() {
		if (this.pInicio.getY() == this.pFinal.getY()) {
			return Tipo.HORIZONTAL;

		} else if (this.pInicio.getX() == this.pFinal.getX()) {
			return Tipo.VERTICAL;

		} else if (Math.abs(this.pFinal.getX() - this.pInicio.getX()) == Math
				.abs(this.pFinal.getY() - this.pInicio.getY())) {
			return Tipo.DIAGONAL;

		} else {
			return Tipo.INCLINADA;
		}
	}

	// Metodos getters e setters

	public Ponto2D getppIniciocio() {
		return pInicio;
	}

	public void setppIniciocio(Ponto2D pInicio) {
		this.pInicio = pInicio;
	}

	public Ponto2D getpFinal() {
		return pFinal;
	}

	public void setpFinal(Ponto2D pFinal) {
		this.pFinal = pFinal;
	}

	public Color getCor() {
		return cor;
	}

	public void setCor(Color cor) {
		this.cor = cor;
	}
}
